#include "thread.h"
#include "string.h"
#include "../kernel/memory.h"
#include "../kernel/interrupt.h"
#include "../kernel/debug.h"

extern void switch_task(struct task_pcb* cur, struct task_pcb* next);

#define TASK_PCB_MAGIC 0x65498732

struct list ready_task_list;
struct list all_task_list;

static void thread_start(thread_func* func, void* arg)
{
	intr_enable();
	
	func(arg);
}

struct task_pcb* running_thread()
{
	void* esp = NULL;
	
	asm volatile (
		"mov %%esp, %0;"
		: "=g"(esp)
		);
	
	return (struct task_pcb*)((uint32_t)esp & 0xfffff000);
}

static void init_thread_pcb(struct task_pcb* pcb, const char* name, uint8_t priority)
{
	pcb->stack = (void*)pcb + 0x1000 - sizeof(struct intr_stack) - sizeof(struct thread_stack);
	pcb->status = TASK_READY;
	
	strcpy(pcb->name, name);
	pcb->name[sizeof(pcb->name) -1] = '\0';
	
	pcb->priority = priority;
	pcb->ticks = priority;
	pcb->total_ticks = 0;
	
	list_append(&ready_task_list, &pcb->ready_tag);
	list_append(&all_task_list, &pcb->all_tag);
	
	pcb->page = NULL;
	pcb->magic = TASK_PCB_MAGIC;
}

static void init_thread_stack(struct task_pcb* pcb, thread_func* func, void* arg)
{
	struct thread_stack* stack = pcb->stack;
	
	stack->ebp = 0;
	stack->ebx = 0;
	stack->edi = 0;
	stack->esi = 0;
	
	stack->ret = thread_start;
	stack->unused_ret = NULL;
	stack->func = func;
	stack->arg = arg;
}

static void make_main_thread()
{
	struct task_pcb* main_pcb = running_thread();
	
	init_thread_pcb(main_pcb, "main", 31);
	
	main_pcb->status = TASK_RUNNING;
	list_remove(&main_pcb->ready_tag);
}

static void stack_error()
{
	set_cursor(0);
	int cursor_pos = 0;
	while(cursor_pos < 320)
	{
		print_char(' ');
		cursor_pos++;
	}

	set_cursor(0);
	print_string("!!!!!!!      stack magic error  !!!!!!!!\n");
   
	while(1);
}

void schedule(void)
{
	ASSERT(intr_get_status() == INTR_OFF);
	
	struct task_pcb* pcb = running_thread();
	
	if (pcb->magic != TASK_PCB_MAGIC)
	{
		stack_error();
	}
	
	if (pcb->status == TASK_RUNNING)
	{
		pcb->ticks = pcb->priority;
		pcb->status = TASK_READY;
		
		list_append(&ready_task_list, &pcb->ready_tag);
	}
	
	struct list_item* item = list_pop(&ready_task_list);
	struct task_pcb* next = container_of(struct task_pcb, ready_tag, item);
	next->status = TASK_RUNNING;
	
	switch_task(pcb, next);
}

void thread_init(void)
{
	list_init(&ready_task_list);
	list_init(&all_task_list);
	
	make_main_thread();
}

int8_t thread_create(const char* name, uint8_t priority, thread_func* func, void* arg)
{
	struct task_pcb* pcb;
	enum intr_status intr_status;
	
	if (NULL == name || priority == 0 || NULL == func)
		return -1;
	
	pcb = (struct task_pcb*)malloc_page(PF_KERNEL, 1);
	
	if (NULL == pcb)
		return -1;
	
	intr_status = intr_disable();
	
	init_thread_pcb(pcb, name, priority);
	init_thread_stack(pcb, func, arg);
	
	intr_set_status(intr_status);
	
	/*print_string("start pr");
	print_char('\n');
	{
		struct list_item* item = all_task_list.head.next;
		while (item != &all_task_list.tail)
		{
			struct task_pcb* next = container_of(struct task_pcb, all_tag, item);
			print_string(next->name);
			print_char('\n');
			print_hex(next);
			print_char('\n');
			item = item->next;
		}
	}
	while(1);*/
	
	return 0;
}

